#ifndef __SERIAL_H 
#define __SERIAL_H 
#include <Windows.h>
class CSerial 
{ 
	// Class enumerations 
public: 
	// Communication event 
	typedef enum 
	{ 
		EEventUnknown = -1, // Unknown event 
			EEventNone = 0, // Event trigged without cause 
			EEventBreak = EV_BREAK, // A break was detected on input 
			EEventCTS = EV_CTS, // The CTS signal changed state 
			EEventDSR = EV_DSR, // The DSR signal changed state 
			EEventError = EV_ERR, // A line-status error occurred 
			EEventRing = EV_RING, // A ring indicator was detected 
			EEventRLSD = EV_RLSD, // The RLSD signal changed state 
			EEventRecv = EV_RXCHAR, // Data is received on input 
			EEventRcvEv = EV_RXFLAG, // Event character was received on input 
			EEventSend = EV_TXEMPTY, // Last character on output was sent 
			EEventPrinterError = EV_PERR, // Printer error occured 
			EEventRx80Full = EV_RX80FULL, // Receive buffer is 80 percent full 
			EEventProviderEvt1 = EV_EVENT1, // Provider specific event 1 
			EEventProviderEvt2 = EV_EVENT2, // Provider specific event 2 
	} 
	EEvent; 
	
	// Baudrate 
	typedef enum 
	{ 
		EBaudUnknown = -1, // Unknown 
			EBaud110 = CBR_110, // 110 bits/sec 
			EBaud300 = CBR_300, // 300 bits/sec 
			EBaud600 = CBR_600, // 600 bits/sec 
			EBaud1200 = CBR_1200, // 1200 bits/sec 
			EBaud2400 = CBR_2400, // 2400 bits/sec 
			EBaud4800 = CBR_4800, // 4800 bits/sec 
			EBaud9600 = CBR_9600, // 9600 bits/sec 
			EBaud14400 = CBR_14400, // 14400 bits/sec 
			EBaud19200 = CBR_19200, // 19200 bits/sec (default) 
			EBaud38400 = CBR_38400, // 38400 bits/sec 
			EBaud56000 = CBR_56000, // 56000 bits/sec 
			EBaud57600 = CBR_57600, // 57600 bits/sec 
			EBaud115200 = CBR_115200, // 115200 bits/sec 
			EBaud128000 = CBR_128000, // 128000 bits/sec 
			EBaud256000 = CBR_256000, // 256000 bits/sec 
	} 
	EBaudrate; 
	
	// Data bits (5- 
	typedef enum 
	{ 
		EDataUnknown = -1, // Unknown 
			EData5 = 5, // 5 bits per byte 
			EData6 = 6, // 6 bits per byte 
			EData7 = 7, // 7 bits per byte 
			EData8 = 8 // 8 bits per byte (default) 
	} 
	EDataBits; 
	
	// Parity scheme 
	typedef enum 
	{ 
		EParUnknown = -1, // Unknown 
			EParNone = NOPARITY, // No parity (default) 
			EParOdd = ODDPARITY, // Odd parity 
			EParEven = EVENPARITY, // Even parity 
			EParMark = MARKPARITY, // Mark parity 
			EParSpace = SPACEPARITY // Space parity 
	} 
	EParity; 
	
	// Stop bits 
	typedef enum 
	{ 
		EStopUnknown = -1, // Unknown 
			EStop1 = ONESTOPBIT, // 1 stopbit (default) 
			EStop1_5 = ONE5STOPBITS,// 1.5 stopbit 
			EStop2 = TWOSTOPBITS // 2 stopbits 
	} 
	EStopBits; 
	
	// Handshaking 
	typedef enum 
	{ 
		EHandshakeUnknown = -1, // Unknown 
			EHandshakeOff = 0, // No handshaking 
			EHandshakeHardware = 1, // Hardware handshaking (RTS/CTS) 
			EHandshakeSoftware = 2 // Software handshaking (XON/XOFF) 
	} 
	EHandshake; 
	
	// Timeout settings 
	typedef enum 
	{ 
		EReadTimeoutUnknown = -1, // Unknown 
			EReadTimeoutNonblocking = 0, // Always return immediately 
			EReadTimeoutBlocking = 1 // Block until everything is retrieved 
	} 
	EReadTimeout; 
	
	// Communication errors 
	typedef enum 
	{ 
		EErrorUnknown = 0, // Unknown 
			EErrorBreak = CE_BREAK, // Break condition detected 
			EErrorFrame = CE_FRAME, // Framing error 
			EErrorIOE = CE_IOE, // I/O device error 
			EErrorMode = CE_MODE, // Unsupported mode 
			EErrorOverrun = CE_OVERRUN, // Character buffer overrun, next byte is lost 
			EErrorRxOver = CE_RXOVER, // Input buffer overflow, byte lost 
			EErrorParity = CE_RXPARITY,// Input parity error 
			EErrorTxFull = CE_TXFULL // Output buffer full 
	} 
	EError; 
	
	// Port availability 
	typedef enum 
	{ 
		EPortUnknownError = -1, // Unknown error occurred 
			EPortAvailable = 0, // Port is available 
			EPortNotAvailable = 1, // Port is not present 
			EPortInUse = 2 // Port is in use 
			
	} 
	EPort; 
	
	// Construction 
public: 
	CSerial(); 
	virtual ~CSerial(); 
	
	// Operations 
public: 
	// Check if particular COM-port is available (static method). 
	static EPort CheckPort (LPCTSTR lpszDevice); 
	
	// Open the serial communications for a particular COM port. You 
	// need to use the full devicename (i.e. "COM1") to open the port. 
	// It's possible to specify the size of the input/output queues. 
	virtual LONG Open (LPCTSTR lpszDevice, DWORD dwInQueue = 0, DWORD dwOutQueue = 0); 
	
	// Close the serial port. 
	virtual LONG Close (void); 
	
	// Setup the communication settings such as baudrate, databits, 
	// parity and stopbits. The default settings are applied when the 
	// device has been opened. Call this function if these settings do 
	// not apply for your application. If you prefer to use integers 
	// instead of the enumerated types then just cast the integer to 
	// the required type. So the following two initializations are 
	// equivalent: 
	// 
	// Setup(EBaud9600,EData8,EParNone,EStop1) 
	// 
	// or 
	// 
	// Setup(EBaudrate(9600),EDataBits(,EParity(NOPARITY),EStopBits(ONESTOPBIT)) 
	// 
	// In the latter case, the types are not validated. So make sure 
	// that you specify the appropriate values. 
	virtual LONG Setup (EBaudrate eBaudrate = EBaud9600, 
		EDataBits eDataBits = EData8, 
		EParity eParity = EParNone, 
		EStopBits eStopBits = EStop1); 
	
	// Set/clear the event character. When this byte is being received 
	// on the serial port then the EEventRcvEv event is signalled, 
	// when the mask has been set appropriately. If the fAdjustMask flag 
	// has been set, then the event mask is automatically adjusted. 
	virtual LONG SetEventChar (BYTE bEventChar, bool fAdjustMask = true); 
	
	// Set the event mask, which indicates what events should be 
	// monitored. The WaitEvent method can only monitor events that 
	// have been enabled. The default setting only monitors the 
	// error events and data events. An application may choose to 
	// monitor CTS. DSR, RLSD, etc as well. 
	virtual LONG SetMask (DWORD dwMask = EEventBreak|EEventError|EEventRecv); 
	
	// The WaitEvent method waits for one of the events that are 
	// enabled (see SetMask). 
	virtual LONG WaitEvent (DWORD dwTimeout = INFINITE); 
	
	// Setup the handshaking protocol. There are three forms of 
	// handshaking: 
	// 
	// 1) No handshaking, so data is always send even if the receiver 
	// cannot handle the data anymore. This can lead to data loss, 
	// when the sender is able to transmit data faster then the 
	// receiver can handle. 
	// 2) Hardware handshaking, where the RTS/CTS lines are used to 
	// indicate if data can be sent. This mode requires that both 
	// ports and the cable support hardware handshaking. Hardware 
	// handshaking is the most reliable and efficient form of 
	// handshaking available, but is hardware dependant. 
	// 3) Software handshaking, where the XON/XOFF characters are used 
	// to throttle the data. A major drawback of this method is that 
	// these characters cannot be used for data anymore. 
	virtual LONG SetupHandshaking (EHandshake eHandshake); 
	
	// Read operations can be blocking or non-blocking. You can use 
	// this method to setup wether to use blocking or non-blocking 
	// reads. Non-blocking reads is the default, which is required 
	// for most applications. 
	// 
	// 1) Blocking reads, which will cause the 'Read' method to block 
	// until the requested number of bytes have been read. This is 
	// useful if you know how many data you will receive. 
	// 2) Non-blocking reads, which will read as many bytes into your 
	// buffer and returns almost immediately. This is often the 
	// preferred setting. 
	virtual LONG SetupReadTimeouts (EReadTimeout eReadTimeout); 
	
	// Obtain communication settings 
	virtual EBaudrate GetBaudrate (void); 
	virtual EDataBits GetDataBits (void); 
	virtual EParity GetParity (void); 
	virtual EStopBits GetStopBits (void); 
	virtual EHandshake GetHandshaking (void); 
	virtual DWORD GetEventMask (void); 
	virtual BYTE GetEventChar (void); 
	
	// Write data to the serial port. Note that we are only able to 
	// send ANSI strings, because it probably doesn't make sense to 
	// transmit Unicode strings to an application. 
	virtual LONG Write (const void* pData, size_t iLen, DWORD* pdwWritten = 0, DWORD dwTimeout = INFINITE); 
	
	// Read data from the serial port. Refer to the description of 
	// the 'SetupReadTimeouts' for an explanation about (non) blocking 
	// reads and how to use this. 
	virtual LONG Read (void* pData, size_t iLen, DWORD* pdwRead = 0, DWORD dwTimeout = INFINITE); 
	
	// Send a break 
	LONG Break (void); 
	
	// Determine what caused the event to trigger 
	EEvent GetEventType (void); 
	
	// Obtain the error 
	EError GetError (void); 
	
	// Obtain the COMM and event handle 
	HANDLE GetCommHandle (void) { return m_hFile; } 
	
	// Check if com-port is opened 
	bool IsOpen (void) const { return (m_hFile != 0); } 
	
	// Obtain last error status 
	LONG GetLastError (void) const { return m_lLastError; } 
	
	// Obtain CTS/DSR/RING/RLSD settings 
	bool GetCTS (void); 
	bool GetDSR (void); 
	bool GetRing (void); 
	bool GetRLSD (void); 
	
	// Purge all buffers 
	LONG Purge (void); 
	
protected: 
	// Internal helper class which wraps DCB structure 
	class CDCB : public DCB 
	{ 
	public: 
		CDCB() { DCBlength = sizeof(DCB); } 
	}; 
	
	// Attributes 
	protected: 
		LONG m_lLastError; // Last serial error 
		HANDLE m_hFile; // File handle 
		EEvent m_eEvent; // Event type 
		DWORD m_dwEventMask; // Event mask 
		OVERLAPPED	m_ol;
}; 

#endif // __SERIAL_H